﻿using System.Collections.Generic;
using SymbolIntegration.core.Helpers;

namespace SymbolIntegration.core.Expressions
{
    public enum FunctionType
    {
        Exp,
        Ln,
        Sin,
        Cos,
        Tg,
        Ctg,
        Atg,
        Actg,
        Integr
    }

    public abstract class FuncExpression : Expression
    {
        protected Expression m_Argument;

        protected FuncExpression()
        {

        }

        protected FuncExpression(Expression expr)
        {
            m_Argument = expr;
        }

        public abstract FunctionType FunctionType { get; }
        protected abstract string GetFunctionName();
        protected abstract ConstantExpression Apply(double constant);
        protected abstract Expression InnerFuncDifferentiate(string argument);

        public  Expression Argument { get { return m_Argument; } }

        public override ExpressionType Type
        {
            get
            {
                return ExpressionType.Function;
            }
        }

        public override Polinom FormatToPolinomAsDependentFromVariable(string variable)
        {
            return null;
        }

        protected internal override void FillExpressionStatistic(ExpressionStatistic statistic, string integrateVariable)
        {
            m_Argument.FillExpressionStatistic(statistic, integrateVariable);
            var polinom = m_Argument.FormatToPolinom(integrateVariable);
            var funcStatistic = new FunctionStatistic();
            funcStatistic.Type = FunctionType;
            funcStatistic.ArgumentPolinomMaxPower = null;
            if (polinom != null)
            {
                funcStatistic.ArgumentPolinomMaxPower = polinom.GetMaxPower();
            }

            statistic.Functions.Add(funcStatistic);
        }

        public override Expression Substitute(Dictionary<string, Expression> varValues)
        {
            var clone = (FuncExpression)Clone();
            clone.m_Argument = m_Argument.Substitute(varValues);
            var constant = clone.m_Argument as ConstantExpression;
            if (constant != null)
            {
                double value = ((ConstantExpression) constant.Normalize()).Value;
                clone.m_Argument = Apply(value);
            }
            return clone;
        }

        public override Expression Differentiate(string variable)
        {
            Expression result;
            do
            {
                if (!IsDependsFrom(variable))
                {
                    result = new ConstantExpression(0);
                    break;
                }

                result = InnerFuncDifferentiate(variable)*m_Argument.Differentiate(variable);

            } while (false);

            result = result.InvertNegate(IsNegate);
            return result;
        }

        public override Expression Normalize()
        {
            Expression result;
            do
            {
                var normalArgument = m_Argument.Normalize();
                var constExpr = normalArgument as ConstantExpression;
                if (constExpr != null)
                {
                    double value = ((ConstantExpression)constExpr.Normalize()).Value;
                    result = Apply(value);
                    result.InvertNegate(IsNegate).Normalize();
                    break;
                }

                var funcResult = (FuncExpression) Clone();
                funcResult.m_Argument = normalArgument;
                result = funcResult;
            } 
            while (false);

            return result;
        }

        public override bool IsEqual(Expression right)
        {
            bool result = false;
            do
            {
                if (right.IsNegate != IsNegate)
                {
                    break;
                }

                var func = right as FuncExpression;
                if (func == null)
                {
                    break;
                }

                if (func.FunctionType != FunctionType)
                {
                    break;
                }

                if (!m_Argument.IsEqual(func.m_Argument))
                {
                    break;
                }

                result = true;
            }
            while (false);

            return result;
        }

        public override string ToString()
        {
            string result = IsNegate ? "-":"";
            result += GetFunctionName()+"(" + m_Argument + ")";
            return result;
        }

        protected internal override void RebuildDependancyCache()
        {
            m_Cache.Varibles.Clear();
            if (!m_Argument.m_Cache.IsValid)
            {
                m_Argument.RebuildDependancyCache();
            }
            m_Cache.Varibles.UnionWith(m_Argument.m_Cache.Varibles);
        }

        protected void InnerParseItself(string expression, int position, int size)
        {
            m_Argument = ParseUtils.Parse(expression, position, size);
        }
    }
}